/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：太平金科-基础组件封装层-分布式协调工具Zookeeper-缺省实现
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taipingframework.boot.zookeeper.autoconfigure;

import com.taipingframework.boot.zookeeper.DefaultZkClientApi;
import com.taipingframework.boot.zookeeper.ZkClientApi;
import com.taipingframework.boot.zookeeper.listener.ZookeeperWatcher;
import com.taipingframework.boot.zookeeper.properties.ZookeeperProperty;
import lombok.extern.slf4j.Slf4j;
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.ZooKeeper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

/**
 *
 * 1、分布式协调工具zookeeper应用场景
 * (1) 数据的发布与订阅(即，分布式配置中心)
 * (2) 负载均衡
 * (3) 命名服务(即，分布式注册中心)
 * (4) 分布式通知/协调
 * 解读：① 另一种心跳检测机制(ping); ② 另一种系统调度模式; ③ 另一种工作汇报模式;
 * (5) 集群管理与Master选举
 * (6) 分布式锁
 *
 * 2、zookeeper实现分布式锁
 * 流程原理：
 * ①临时节点随着会话连接的失效自动删除；
 * ②多个服务在zk上创建同一个临时节点，谁能够创建成功，谁就能拿到锁；
 * ③其它服务没有在zk上创建节点成功，就继续等待(信号量CountDownLatch)；
 * ④使用事件通知获取节点被删除的信号，唤醒程序或解除阻塞，重新尝试获取锁资源；
 * ⑤通过结束当前zk会话的方式删除临时节点
 * 场景：分布式场景，生成全局唯一ID
 *
 * 3、负载均衡
 * (1) 负载均衡算法：权重、IP绑定、轮询、随机
 * (2) zookeeper实现负载均衡
 *
 * 4、基于Zookeeper注册中心实现选举策略与哨兵机制
 * 流程原理：
 * ① 服务启动的时候在ZK上创建相同的一个临时节点
 * ② 谁能够创建成功，谁就是主服务器
 * ③ 使用事件监听临时节点是否被删除，如果接收到临时节点被删除的事件通知，重新开始选举（即，重新开始创建临时节点）。
 */
@Slf4j
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(
        prefix = "application.zk",
        name = {"connect-string"}
)
public class ZookeeperAutoConfiguration {

    /**
     * Zookeeper连接配置
     */
    @Bean
    @ConfigurationProperties(prefix = "application.zk")
    public ZookeeperProperty zookeeperProperty() {
        return new ZookeeperProperty();
    }

    /**
     * Zookeeper事件监听
     */
    @Bean("zkWatcher")
    public ZookeeperWatcher zkWatcher() {
        return new ZookeeperWatcher();
    }

    /**
     * Zookeeper连接对象
     */
    @Bean(name = "zk", destroyMethod = "close")
    public ZooKeeper zk(ZookeeperProperty zkProp,
                        ZookeeperWatcher zkWatcher) throws IOException {
        return new ZooKeeper(zkProp.getConnectString(), zkProp.getSessionTimeout(), zkWatcher);
    }

    /**
     * Zookeeper客户端对象
     */
    @Bean("zkClient")
    public ZkClient zkClient(ZookeeperProperty zkProp) {
        return new ZkClient(zkProp.getConnectString(), zkProp.getSessionTimeout());
        //zkClient.createPersistent();
        //zkClient.createPersistentSequential();

        //zkClient.createEphemeral();
        //zkClient.createEphemeralSequential()

        //zkClient.subscribeDataChanges();
        //zkClient.unsubscribeDataChanges();

        //zkClient.subscribeStateChanges();
        //zkClient.unsubscribeStateChanges();

        //zkClient.subscribeChildChanges();
        //zkClient.unsubscribeChildChanges();
    }

    /**
     * Zookeeper客户端API默认实现
     */
    @Bean
    public ZkClientApi zkClientApi(ZooKeeper zooClient) {
        return new DefaultZkClientApi(zooClient);
    }

}
